package world.snowcrystal.aa;

import com.google.gson.Gson;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import world.snowcrystal.dto.ApplicationMessage;
import world.snowcrystal.properties.AuthenticationProperties;
import world.snowcrystal.utils.RequestUtils;

import java.io.IOException;


/**
 * 当这个 handler 被触发，则会尝试清除 UA 的 Cookie 并
 * forward 到一个指定的 URL；这个类参考了 {@link org.springframework.security.web.authentication.ForwardAuthenticationFailureHandler}
 *
 * @author tianqing
 * @since 1.0
 */

@Log4j2
public class RememberMeAuthenticationFailureHandler implements AuthenticationFailureHandler {

    private AuthenticationProperties properties;
    private Gson gson = new Gson();

    private String forwardUrl;

    public RememberMeAuthenticationFailureHandler(AuthenticationProperties properties, String forwardUrl) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), () -> "'" + forwardUrl + "' is not a valid forward URL");
        this.forwardUrl = forwardUrl;
        this.properties = properties;
    }


    /**
     * Called when an authentication attempt fails.
     *
     * @param request   the request during which the authentication attempt occurred.
     * @param response  the response.
     * @param exception the exception which was thrown to reject the authentication
     *                  request.
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response,
                                        AuthenticationException exception) throws IOException,
            ServletException {

        log.info("failed to authenticate user({}): {}" , RequestUtils.getRequestUsername(request), exception.getMessage());
        if (exception instanceof InternalAuthenticationServiceException) {
            // 服务器内部错误

        } else {
//            clearTokenIfPresent(request, response);
        }


        //TODO 这样做耦合太强了，因为之后需要配置跨域等
        // 还是转发到某个 Controller 上进行统一设置
        // 但是不能在这里进行 forward ，这会导致循环问题
        // 所以，目前，只能使用这种不太优雅的方式

        request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception);
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/json");
        response.setCharacterEncoding("UTF-8");
        gson.toJson(ApplicationMessage.needLogin(exception.getMessage()), response.getWriter());
//        request.getRequestDispatcher("/user/this?error=1").forward(request, response);
    }

    private void clearTokenIfPresent(HttpServletRequest request,
                                     HttpServletResponse response) {
        Cookie cookie;
        //see SUsernamePasswordAuthenticationFilter  #retrieveTokenFromCookie
        Object authenticationTokenCookie = request.getAttribute("AuthenticationTokenCookie");
        if (authenticationTokenCookie != null) {
            cookie = (Cookie) authenticationTokenCookie;
            cookie.setValue(null);
        } else{
            cookie = new Cookie(properties.getRememberMeCookieName(), null);
            cookie.setPath(getCookiePath(request));
            cookie.setDomain(properties.getCookieDomain());
            cookie.setSecure(properties.getCookieSecure());
        }
        cookie.setMaxAge(0);
        response.addCookie(cookie);
    }

    private String getCookiePath(HttpServletRequest request) {
        String contextPath = request.getContextPath();
        return (!contextPath.isEmpty()) ? contextPath : "/";
    }


}
